package serialisation;

import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import reservation.Billet;
import reservation.Client;
import reservation.Trajet;
import reservation.Transport;
import data.Condition;
import data.Ville;

public class DataManager {
	/**
	 * Liste des trajets
	 */
	private static LinkedList<Trajet> trajets = new LinkedList<Trajet>();

	/**
	 * Liste des transports
	 */
	private static LinkedList<Transport> transports = new LinkedList<Transport>();

	/**
	 * Liste des serializers
	 */
	private static List<Serializer> serializers;

	/**
	 * Base du nom des fichiers qui contiennent les serialisations
	 */
	private static String basefilename = "Reservations";

	static {
		initSerializer();
	}

	/**
	 * Initialise les serializers (Public pour les tests)
	 */
	public static void initSerializer() {
		serializers = new ArrayList<Serializer>();
		serializers.add(new SerializeXML(basefilename));
		serializers.add(new SerializeText(basefilename));
		serializers.add(new SerializeObject(basefilename));
	}

	/**
	 * Lance les serialisations
	 * 
	 * @return True si ok sinon false
	 */
	public static boolean store() {
		List<List<?>> all = new LinkedList<List<?>>();
		all.add(transports);
		all.add(trajets);
		boolean result = true;
		for (Serializer s : serializers) {
			result = s.store(transports, trajets);
		}
		return result;
	}

	/**
	 * Load la base de donnée. On utilise serialize object car c'est le plus
	 * simple
	 * 
	 * @return True
	 */
	public static boolean load() {
		return serializers.get(0).load(transports, trajets);
	}

	/**
	 * récupère la liste de trajets dans une chaine de caractère.
	 * 
	 * @return une chaine de caractère formatée qui contient la liste de
	 *         trajets.
	 */
	public static String listeTrajets() {
		StringBuilder sb = new StringBuilder();
		for (Trajet t : trajets) {
			sb.append(t.toString());
		}
		return sb.toString();
	}

	/**
	 * récupère la liste de transports dans une chaine de caractère.
	 * 
	 * @return une chaine de caractère formatée qui contient la liste de
	 *         transports.
	 */
	public static String listeTransports() {
		StringBuilder sb = new StringBuilder();
		for (Transport t : transports) {
			sb.append(t.toString());
		}
		return sb.toString();
	}

	/**
	 * Overloading store : Méthode store pour l'objet de type transport
	 * 
	 * @param t
	 *            Le transport à ajouter dans le fichier texte
	 */
	public static boolean store(Transport t) {
		return transports.add(t);
	}

	/**
	 * Overloading : Méthode store pour l'objet de type transport
	 * 
	 * @param t
	 *            Le trajet à ajouter dans le fichier texte
	 */
	public static boolean store(Trajet t) {
		return trajets.add(t);
	}

	/**
	 * Overloading store : Ajouter un client dans un trajet donné
	 * 
	 * @param b
	 *            Le billet cible
	 * 
	 * @return true si le voyageur est bien ajouté, false sinon (existe déjà)
	 * @throws IOException
	 * 
	 */
	public static boolean store(Billet b) {
		if (canReserve(b)) {
			for (Trajet tr : trajets) {
				if (tr.getIdentTrajet() == b.getTrajet().getIdentTrajet()) {
					tr.addBillet(b);
					return store();
				}
			}
		}

		return false;
	}

	public static boolean canReserve(Billet b) {
		if (b.getTrajet().getBillet().contains(b)) {
			System.out.println("Un billet est deja reserver à ce nom la !");
			return false;
		}
		if (!b.getTrajet().existPlaces()) {
			System.out
					.println("Il ne reste plus de places disponibles pour ce trajet");
			return false;
		}
		return true;
	}

	/**
	 * Supprimer le voyageur cible dans le trajet cible
	 * 
	 * @param b
	 *            Le billet cible
	 * 
	 * @return true si le voyageur est supprimé, false sinon (le voyageur
	 *         n'existe pas dans le trajet)
	 */
	public static boolean delete(Billet b) {
		// Supprimer le client de la liste courante de la classe éponyme
		for (int i = 0; i < trajets.size(); i++) {
			Trajet tr = trajets.get(i);
			// Vérifier que c'est le bon trajet
			if (tr.getIdentTrajet() == b.getTrajet().getIdentTrajet()) {
				if (b.getCondition() != Condition.DEFINITIF) {
					if (tr.deleteBillet(b)) {
						return store();
					} else {
						return false;
					}
				} else {
					System.out
							.println("Erreur: Ce billet ne peut etre supprimé");
					return false;
				}
			}
		}

		System.out.println("Le client d'identifiant "
				+ b.getClient().getLogin()
				+ " n'a pas été trouvé dans ce trajet");
		return false;
	}

	/**
	 * Supprimer le trajet cible
	 * 
	 * @param t
	 *            Le trajet cible
	 * @return true si le trajet existe, false sinon
	 */
	public static boolean delete(Trajet t) {
		// Supprimer le client de la liste courante de la classe éponyme
		for (int i = 0; i < trajets.size(); i++) {
			Trajet tr = trajets.get(i);
			// Vérifier que c'est le bon trajet
			if (tr.getIdentTrajet() == t.getIdentTrajet()) {
				// Parcourir la liste des clients
				for (int j = 0; j < tr.getBillet().size(); j++) {
					// Client cl = tr.getVoyageurs().get(j);
					// Supprimer les clients de la liste
					tr.getBillet().remove(j);
				}
				trajets.remove(i);
				return true;
			}
		}

		System.out.println("Le trajet n'a pas été trouvé");
		return false;
	}

	/**
	 * Supprimer le transport cible
	 * 
	 * @param t
	 *            Le transport cible
	 * @return true si le transport existe, false sinon
	 */
	public static boolean delete(Transport t) {
		// Supprimer le client de la liste courante de la classe éponyme
		for (int i = 0; i < transports.size(); i++) {
			Transport tp = transports.get(i);
			// Vérifier que c'est le bon transport
			if (tp.getIdentTransport() == t.getIdentTransport()) {
				// Parcourir la liste des trajets
				for (int j = 0; j < trajets.size(); j++) {
					Trajet tr = trajets.get(j);
					// Supprimer les trajets associés
					if (tp.getIdentTransport() == tr.getIdentTrajet()) {
						trajets.remove(j);
					}
				}
				transports.remove(i);
				return true;
			}
		}
		System.out.println("Le transport recherché n'existe pas !");
		return false;
	}

	/**
	 * Modifier le transport cible
	 * 
	 * @param t
	 *            Le transport cible
	 * @return true or false
	 */
	public static boolean alter(Transport t) {
		for (int i = 0; i < transports.size(); i++) {
			Transport tp = transports.get(i);
			// Vérifier que c'est le bon trajet
			if (tp.getIdentTransport() == t.getIdentTransport()) {
				transports.remove(i);
				transports.add(t);
				return true;
			}
		}
		System.out.println("Le transport recherché n'existe pas !");
		return false;
	}

	/**
	 * Modifier le trajet cible
	 * 
	 * @param t
	 *            Le trajet cible
	 * @return true or false
	 */
	public static boolean alter(Trajet t) {
		for (int i = 0; i < trajets.size(); i++) {
			Trajet tr = trajets.get(i);
			// Vérifier que c'est le bon trajet
			if (tr.getIdentTrajet() == t.getIdentTrajet()) {
				trajets.remove(i);
				trajets.add(t);
				return true;
			}
		}
		System.out.println("Le trajet recherché n'existe pas !");
		return false;
	}

	/**
	 * Retrouver un transport par un son identifiant
	 * 
	 * @param id
	 * @return le transport s'il est trouvé, null sinon;
	 */
	public static Transport getTransportById(int id) {
		Transport found = null;
		for (Transport tmp : transports) {
			if (tmp.getIdentTransport() == id)
				found = tmp;
		}
		return found;
	}

	/**
	 * Retrouver un transport par un son identifiant
	 * 
	 * @param id
	 * @return le trajet s'il est trouvé, null sinon;
	 */
	public static Trajet getTrajetById(int id) {
		Trajet found = null;
		for (Trajet tmp : trajets) {
			if (tmp.getIdentTrajet() == id)
				found = tmp;
		}
		return found;
	}

	/**
	 * Récupérer tous les trajets ou un client est désigné voyageur
	 * 
	 * @param c
	 *            Le client recherché
	 * @return la liste des trajets retrouvés
	 */
	public static List<Trajet> getTrajetsByClient(Client c) {
		List<Trajet> resultat = new ArrayList<Trajet>();
		for (Trajet t : trajets) {
			if (t.exists(c))
				resultat.add(t);
		}
		if (resultat.size() == 0)
			System.out.println("Aucun résultat trouvé pour le client de login "
					+ c.getLogin());
		return resultat;
	}

	/**
	 * Récupérer tous les trajets qu'un transport effectue
	 * 
	 * @param t
	 *            Le transport recherché
	 * @return la liste des transports retrouvés
	 */
	public static List<Trajet> getTrajetsByTransport(Transport t) {
		List<Trajet> resultat = new ArrayList<Trajet>();
		for (Trajet tr : trajets) {
			if (tr.getTransport().getIdentTransport() == t.getIdentTransport())
				resultat.add(tr);
		}
		return resultat;
	}

	/**
	 * Récupérer les transports qui ont une place disponible pour un trajet
	 * d'une date donnée
	 * 
	 * @param d
	 *            La date cible
	 * @return la liste des transports trouvés
	 */
	public static List<Transport> getTransportsByDate(Date d) {
		List<Transport> resultat = new ArrayList<Transport>();
		DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy à HH:mm");
		for (Trajet tr : trajets) {

			if (tr.existPlaces()
					&& String.valueOf(tr.getDate()).equals(
							String.valueOf(dateFormat.format(d)))
					&& !resultat.contains(tr.getTransport()))
				resultat.add(tr.getTransport());
		}
		return resultat;
	}

	/**
	 * Récupérer les transports qui ont une place disponible pour un départ dans
	 * une ville donnée
	 * 
	 * @param v
	 *            La ville cible
	 * @return la liste des transports trouvés
	 */
	public static List<Transport> getTransportsByVille(Ville v) {
		List<Transport> resultat = new ArrayList<Transport>();
		for (Trajet tr : trajets) {
			if (tr.existPlaces() && tr.getDepart().equals(v)
					&& !resultat.contains(tr.getTransport()))
				resultat.add(tr.getTransport());
		}
		return resultat;
	}

	/**
	 * Récupérer les trajets qui ont la même date
	 * 
	 * @param d
	 *            La date cible
	 * @return la liste des trajets trouvés
	 */
	public static List<Trajet> getTrajetsByDate(Date d) {
		List<Trajet> resultat = new ArrayList<Trajet>();
		DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy à HH:mm");
		for (Trajet tr : trajets) {
			if (String.valueOf(tr.getDate()).equals(
					String.valueOf(dateFormat.format(d))))
				resultat.add(tr);
		}
		return resultat;
	}

	/**
	 * Récupérer les trajets qui ont la même ville de départ
	 * 
	 * @param v
	 *            La ville cible
	 * @return la liste des trajets trouvés
	 */
	public static List<Trajet> getTrajetsByVille(Ville v) {
		List<Trajet> resultat = new ArrayList<Trajet>();
		for (Trajet tr : trajets) {
			if (String.valueOf(tr.getDepart()).equals(String.valueOf(v)))
				resultat.add(tr);
		}
		return resultat;
	}

	/**
	 * Récupérer les trajets à partir d'une date de départ, pour un aller et un
	 * retour
	 * 
	 * @param d
	 *            La ville de départ
	 * @param a
	 *            La ville d'arrivée
	 * @param date
	 *            La date de départ
	 * @return la liste des trajets resultants de l'opération
	 * @throws ParseException
	 */
	@SuppressWarnings("deprecation")
	public static List<Trajet> getTrajetsLikeSncf(Ville d, Ville a, Date date)
			throws ParseException {
		List<Trajet> resultat = new ArrayList<Trajet>();
		for (Trajet tr : trajets) {
			if (tr.getDepart() == d
					&& tr.getArrivee() == a
					&& Trajet.toFormatedDate(tr.getDate()).getYear() == date
							.getYear()
					&& Trajet.toFormatedDate(tr.getDate()).getMonth() == date
							.getMonth()
					&& Trajet.toFormatedDate(tr.getDate()).getDay() == date
							.getDay()
					&& date.getHours() <= Trajet.toFormatedDate(tr.getDate())
							.getHours())
				resultat.add(tr);
		}
		return resultat;
	}

	/**
	 * Obtenir un client à travers son login
	 * 
	 * @param login
	 *            Le login recherché
	 * @return le client recherché.
	 */
	public static Client getClientByLogin(String login) {
		for (Trajet t : trajets) {
			for (Billet b : t.getBillet()) {
				if (b.getClient().getLogin().equals(login)) {
					return b.getClient();
				}
			}
		}
		return null;
	}

	/**
	 * récupère la liste des billets réservés par le client passé en paramètre
	 * 
	 * @param c
	 *            le client
	 * @return la liste des billets réservés par le client
	 */
	public static List<Billet> obtenirBillets(Client c) {
		List<Billet> billets = new ArrayList<Billet>();
		for (Trajet trajet : trajets) {
			for (Billet billet : trajet.getBillet()) {
				if (billet.getClient().equals(c)) {
					billets.add(billet);
				}
			}
		}
		return billets;
	}

	public static List<Trajet> getTrajets() {
		return trajets;
	}

	/* FOR TEST */

	public static void setFile(String file) {
		basefilename = file;
	}
}
